//Keywords Screensaver 
//Copyright (C) 2011 Douglas Morrison 

//This program is free software: you can redistribute it and/or modify it under the terms of 
//the GNU General Public License as published by the Free Software Foundation, either version 3 
//of the License, or (at your option) any later version. This program is distributed in the hope 
//that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 
//of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
//for more details. For a copy of the GNU General Public License, see <http://www.gnu.org/licenses/>. 

//Contact the developer at: 
//http://groups.google.com/group/keywords-screensaver

using System;
using System.IO;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Xml;
using System.Xml.XPath;

namespace scrSaver
{
    //public class SpeechBubble : System.Windows.Forms.PictureBox
    //{

    //}
	public class Form1 : System.Windows.Forms.Form
    {
		private System.Windows.Forms.Timer timer1;
		private System.ComponentModel.IContainer components;
		// Store the mouse coordinates
		private Point mouseCoords;
		// Store the number of displays
        private int displayNum;
        private Panel panel3;

        private Dictionary<string,MyPictureBox> Bubblemap = new Dictionary<string,MyPictureBox>();
        private Dictionary<string, MyLabel> Labelmap = new Dictionary<string, MyLabel>();
        private int Col1x;
        private int Row1y;
        private int sucPlaced = 0;
        private int failCount = 0;
        private int maxfail = 200;
        private int widthMidPanel, heightMidPanel;
        private int imageWidth = 250;
        private int imageHeight = 150;
        private bool supTopChange = false;
        private string[] BubbleName;
        private string[] LabelName;
        private string LatestTopic;
        private string titleText;
        private string yearSearch = Properties.Settings.Default.curYear;
        private string topicSearch = Properties.Settings.Default.curTopic;
        private List<string> wordList = new List<string>();
        private List<string> yearList = new List<string>();
        private List<string> topicList = new List<string>();
        private MyTitle myT;
        private MyCloseButton myCB;
        private keywordsPicker myKWP;
        string quot = Char.ToString((char)22);
        private Timer timer2;
    
		// Random number that will change the position of the PictureBox
		Random rand = new Random();

		// Accept one argurment - the number of displays
		public Form1(int display)
		{
			InitializeComponent();
			// Assign the number to an accessible variable
			displayNum = display;
		}
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
            this.components = new System.ComponentModel.Container();
            this.timer1 = new System.Windows.Forms.Timer(this.components);
            this.panel3 = new System.Windows.Forms.Panel();
            this.timer2 = new System.Windows.Forms.Timer(this.components);
            this.SuspendLayout();
            // 
            // timer1
            // 
            this.timer1.Enabled = true;
            this.timer1.Interval = 20000;
            this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
            // 
            // panel3
            // 
            this.panel3.Dock = System.Windows.Forms.DockStyle.Top;
            this.panel3.Location = new System.Drawing.Point(0, 0);
            this.panel3.Name = "panel3";
            this.panel3.Size = new System.Drawing.Size(800, 75);
            this.panel3.TabIndex = 3;
            this.panel3.Visible = false;
            // 
            // timer2
            // 
            this.timer2.Enabled = true;
            this.timer2.Interval = 10000;
            this.timer2.Tick += new System.EventHandler(this.timer2_Tick);
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.BackColor = System.Drawing.Color.Black;
            this.ClientSize = new System.Drawing.Size(800, 600);
            this.Controls.Add(this.panel3);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            this.Name = "Form1";
            this.ShowInTaskbar = false;
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
            this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseMove);
            this.ResumeLayout(false);

		}
		#endregion

		private void Form1_Load(object sender, System.EventArgs e)
		{
            timer1.Interval = Properties.Settings.Default.RefreshFreq * 60000;
            string myCheckpath = Properties.Settings.Default.KeywordsPath;
            checkKeywordFile();
			// Set the bounds of the form, fill all the screen
			this.Bounds = Screen.AllScreens[displayNum].Bounds;
			// The form should be on top of all
			//TopMost = true;
			// We don't need the cursor
			Cursor.Hide();
            getXMLWordData(yearSearch, topicSearch);
            wordList = MixList<string>(wordList);
            setObjPositions();
            titleText = setTitleText();
            myT = new MyTitle(setTitleText(), widthMidPanel);
            this.Controls.Add(myT);
            myCB = new MyCloseButton(widthMidPanel);
            myCB.Click += new System.EventHandler(closeSaver);
            myKWP = new keywordsPicker();
            myKWP.SetBounds(10, 20, 360, 50);
            myKWP.Load += new System.EventHandler(KWPload);
            ComboBox myTopicCombo = getKWPCombo("TopicCombo");
            ComboBox myYearCombo = getKWPCombo("YearCombo");
            myTopicCombo.SelectedIndexChanged += new System.EventHandler(KWP_Topic_Change);
            myTopicCombo.Click += new System.EventHandler(KWP_Rem_Sup);
            myYearCombo.SelectedIndexChanged += new System.EventHandler(KWP_Year_Change);
		}

        private string setTitleText()
        {
            string mytitle = "Keywords - Year " + Properties.Settings.Default.curYear + System.Environment.NewLine + Properties.Settings.Default.curTopic;
            return mytitle;
        }

        public static void checkKeywordFile()
        {
            if (Properties.Settings.Default.KeywordsPath == "")
            {
                string mydocs = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\keywords-screensaver";
                string keyFile = mydocs + @"\keywords.xml";
                string exFile = mydocs + @"\keywords-example.xml";
                if (Directory.Exists(mydocs) == false)
                {
                    System.IO.Directory.CreateDirectory(mydocs);
                }
                if (File.Exists(keyFile) == false | File.Exists(exFile) == false)
                {

                    Stream objStream = null;

                    FileStream objFileStream = null;

                    try
                    {

                        byte[] abytResource;
                        System.Reflection.Assembly objAssembly = System.Reflection.Assembly.GetExecutingAssembly();

                        string[] myResources = objAssembly.GetManifestResourceNames();

                        if (File.Exists(keyFile) == false)
                        {
                                objStream = objAssembly.GetManifestResourceStream("scrSaver.keywords.xml");
                                abytResource = new Byte[objStream.Length];
                                objStream.Read(abytResource, 0, (int)objStream.Length);
                                objFileStream = new FileStream(keyFile, FileMode.Create);
                                objFileStream.Write(abytResource, 0, (int)objStream.Length);
                                objFileStream.Close();
                                Cursor.Show();
                                MessageBox.Show("A file called \"keywords.xml\" has been created in " + Char.ToString((char)13) + Char.ToString((char)13) + mydocs
        + Char.ToString((char)13) + Char.ToString((char)13) + "You will need to locate and edit this file to customise your keywords.", "Important information");
                        }
                        if (File.Exists(exFile) == false)
                        {
                            objStream = objAssembly.GetManifestResourceStream("scrSaver.keywords-example.xml");
                            abytResource = new Byte[objStream.Length];
                            objStream.Read(abytResource, 0, (int)objStream.Length);
                            objFileStream = new FileStream(exFile, FileMode.Create);
                            objFileStream.Write(abytResource, 0, (int)objStream.Length);
                            objFileStream.Close();
                            Cursor.Show();
                            MessageBox.Show("An example file called \"keywords-example.xml\" has been created in " + Char.ToString((char)13) + Char.ToString((char)13) + mydocs
    + Char.ToString((char)13) + Char.ToString((char)13) + "Please refer to this file to see how keywords should be defined.", "For information");
                        }


                    }

                    catch (Exception)
                    {
                        NullReferenceException myNRE = new NullReferenceException();
                    }

                    finally
                    {
                        if (objFileStream != null)
                        {
                            objFileStream.Close();
                            objFileStream = null;
                        }

                        if (objStream != null)
                        {
                            objStream = null;
                        }
                    }

                }
                Properties.Settings.Default.KeywordsPath = keyFile;
                Properties.Settings.Default.Save();
            }
        }

        private bool RectsOverlap(int x0, int y0, int w0, int h0, int x2, int y2, int w2, int h2)
        {
            if (x0 > (x2 + w2) | (x0 + w0) < x2)
            {
                return false;
            }
            if (y0 > (y2 + h2) | (y0 + h0) < y2)
            {
                return false;
            }
            return true;
        }

        private int[,] positionRandomiser(int minxCoord, int minyCoord, int availWidth, int availHeight, int minWidth, int minHeight)
        {
            int[,] positionDatabase = new int[100, 2];
            int[,] newpositionDatabase;
            sucPlaced = 0;
            for (int i = 0; i < 100; i++)
            {
                bool mycheck = false;
                failCount = 0;
            Retry:
                positionDatabase[i, 0] = rand.Next(minxCoord, availWidth - minWidth);
                positionDatabase[i, 1] = rand.Next(minyCoord, availHeight - minHeight);
                if (i > 0)
                {
                    for (int j = 0; j < i; j++)
                    {
                        mycheck = RectsOverlap(positionDatabase[i, 0], positionDatabase[i, 1], minWidth, minHeight, positionDatabase[j, 0],
                            positionDatabase[j, 1], minWidth, minHeight);
                        if (mycheck == true)
                        {
                            failCount++;
                            if (failCount == maxfail)
                            {
                                goto Moveon;
                            }
                            goto Retry;
                        }
                    }
                    sucPlaced++;
                }
            }
            Moveon:
                        
            newpositionDatabase = new int[sucPlaced,2];

            for (int i = 0; i < sucPlaced; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    newpositionDatabase[i, j] = positionDatabase[i, j];
                }
            }
            return newpositionDatabase;
        }

        private void setObjPositions()
        {   
            widthMidPanel = this.Bounds.Width - 20;
            heightMidPanel = this.Bounds.Height - 10;
            Col1x = 10;
            Row1y = 75;
            
            int[,] mypos = positionRandomiser(Col1x,Row1y,widthMidPanel,heightMidPanel,imageWidth,imageHeight);
            int numObj = sucPlaced;

            if (wordList.Count < numObj)
            {
                numObj = wordList.Count;
            }

            BubbleName = new String[numObj];
            LabelName = new String[numObj];


            for (int i = 0; i < numObj; i++)
            {
                BubbleName[i] = "Bubble" + i;
                Bubblemap.Add(BubbleName[i], new MyPictureBox(mypos[i, 0], mypos[i, 1], imageWidth, imageHeight));
                
                LabelName[i] = "Label" + i;
                Labelmap.Add(LabelName[i], new MyLabel(wordList[i], mypos[i, 0], mypos[i, 1], imageWidth, imageHeight));

                this.Controls.Add(Labelmap[LabelName[i]]);
                this.Controls.Add(Bubblemap[BubbleName[i]]);

            }
        }

        private void getXMLWordData(string myYear, string myTopic)
        {
            try
            {
                wordList.Clear();

                XmlDocument doc = new XmlDocument();
                doc.Load(Properties.Settings.Default.KeywordsPath);

                XmlNodeList nodeListYear;
                XmlNodeList nodeListTopics;
                XmlNodeList nodeListWords;

                XmlNode curNode;

                string myString = "/KEYWORDS/YEAR[text() [position()=1] = \"" + myYear +"\"]"; //author[last-name [position()=1]= "Bob"]
                nodeListYear = doc.SelectNodes(myString);
                curNode = nodeListYear.Item(0);
                nodeListTopics = curNode.SelectNodes("TOPIC[text() [position()=1] = \"" + myTopic + "\"]");
                curNode = nodeListTopics.Item(0);
                nodeListWords = curNode.SelectNodes("WORD");

                foreach (XmlNode node in nodeListWords)
                {
                    wordList.Add(node.InnerText);
                }
            }
            catch 
            {
                if (topicSearch == "")
                {
                    Cursor.Show();
                    MessageBox.Show("Please select a year group or topic to display" + Char.ToString((char)13) + "at the top of the screensaver window.", "Selection Required");
                }
                else
                {
                    Cursor.Show();
                    MessageBox.Show("The xml file that you have selected no longer exists" + Char.ToString((char)13) + "Please check your settings", "Error");
                    Settings setForm = new Settings();
                    setForm.Show();
                }
            }
            finally
            {
            }
        }

         private void getXMLYearData()
        {
            try
            {
                yearList.Clear();

                XmlDocument doc = new XmlDocument();
                doc.Load(Properties.Settings.Default.KeywordsPath);

                XmlNodeList nodeListAllYears;

                nodeListAllYears = doc.SelectNodes("KEYWORDS/YEAR/text()");
                foreach (XmlNode node in nodeListAllYears)
                {
                    yearList.Add(node.InnerText);
                }
            }
            catch 
            {
                Cursor.Show();
                MessageBox.Show("There appears to be a problem with the xml file that you have chosen" + Char.ToString((char)13) + "Please check your file and restart the screensaver", "Error");
                Settings setForm = new Settings();
                setForm.Show();
            }
            finally
            {
            }
        }

         private void getXMLTopicData(string myYear)
         {
             XmlNodeList nodeListYear;
             XmlNode curNode;

             if (myYear == "")
                 return;

             try
             {
                 topicList.Clear();

                 XmlDocument doc = new XmlDocument();
                 doc.Load(Properties.Settings.Default.KeywordsPath);

                 XmlNodeList nodeListAllTopics;
                 XmlAttributeCollection NodeAttributes;
                 
                 string myString = "/KEYWORDS/YEAR[text() = \"" + myYear + "\"]/TOPIC/text()";
                 nodeListAllTopics = doc.SelectNodes(myString);
                 foreach (XmlNode node in nodeListAllTopics)
                 {
                     topicList.Add(node.InnerText);
                 }
                 
                 myString = "/KEYWORDS/YEAR[text() [position()=1] = \"" + myYear + "\"]"; 
                 nodeListYear = doc.SelectNodes(myString);
                 curNode = nodeListYear.Item(0);
                 NodeAttributes = curNode.Attributes;
                 if (NodeAttributes.Count == 0)
                 {
                     XmlAttribute myAttr = doc.CreateAttribute("latest");
                     curNode.Attributes.Append(myAttr);
                     doc.Save(Properties.Settings.Default.KeywordsPath);
                 }
                 foreach (XmlAttribute myAttr in NodeAttributes)
                 {
                     if (myAttr.Name == "latest")
                     {
                         LatestTopic = myAttr.Value;
                     }
                 }

             }
             catch
             {
                 Cursor.Show();
                 MessageBox.Show("There appears to be a problem with the xml file that you have chosen" + Char.ToString((char)13) + "Please check your file and restart the screensaver", "Error");
                 Settings setForm = new Settings();
                 setForm.Show();
             }
             finally
             {
             }
         }

         private void setXMLLatestTopicData(string myYear, string myTopic)
         {
             XmlNodeList nodeListYear;
             XmlNode curNode;

             if (myYear == "" | myTopic == "")
                 return;

             try
             {
                 topicList.Clear();

                 XmlDocument doc = new XmlDocument();
                 doc.Load(Properties.Settings.Default.KeywordsPath);

                 XmlAttributeCollection NodeAttributes;

                 String myString = "/KEYWORDS/YEAR[text() [position()=1] = \"" + myYear + "\"]"; 
                 nodeListYear = doc.SelectNodes(myString);
                 curNode = nodeListYear.Item(0);
                 NodeAttributes = curNode.Attributes;
                 if (NodeAttributes.Count == 0)
                 {
                     XmlAttribute myAttr = doc.CreateAttribute("latest");
                     curNode.Attributes.Append(myAttr);
                     doc.Save(Properties.Settings.Default.KeywordsPath);
                 }
                 foreach (XmlAttribute myAttr in NodeAttributes)
                 {
                     if (myAttr.Name == "latest")
                     {
                         myAttr.Value = myTopic;
                         doc.Save(Properties.Settings.Default.KeywordsPath);
                     }
                 }

             }
             catch
             {
                 Cursor.Show();
                 MessageBox.Show("There appears to be a problem with the xml file that you have chosen" + Char.ToString((char)13) + "Please check your file and restart the screensaver", "Error");
                 Settings setForm = new Settings();
                 setForm.Show();
             }
             finally
             {
             }
         }
		private void Form1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			// If mouseCoords is empty don't close the screen saver
			if(!mouseCoords.IsEmpty)
			{
				// If the mouse actually moved
				if(mouseCoords != new Point(e.X, e.Y))
				{
                    supTopChange = true;
                    timer2.Start();
                    this.Controls.Remove(myT);
                    Cursor.Show();
                    this.Controls.Add(myCB);
                    this.Controls.Add(myKWP);
				}
			}
			// Set the new point where the mouse is
			mouseCoords = new Point(e.X, e.Y);
		}

		// If a key was pressed...
		private void Form1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
		{
			// ...close the screen saver
			this.Close();
		}

		// this is the main timer that controls the keywords refresh rate. By default
        // it is set at 1 min. This can be changed in the Windows screemsaver settings dialog
		private void timer1_Tick(object sender, System.EventArgs e)
		{
            refreshScreen();
		}

        // this is the timer that controls the visibility of the options selection at the top of the screensaver
        private void timer2_Tick(object sender, System.EventArgs e)
        {
            supTopChange = false;
            timer2.Stop();
            this.Controls.Add(myT);
            this.Controls.Remove(myCB);
            this.Controls.Remove(myKWP);
            Cursor.Hide();
        }

        private void refreshScreen()
        {
            int[,] mypos = positionRandomiser(Col1x, Row1y, widthMidPanel, heightMidPanel, imageWidth, imageHeight);
            int numObj = sucPlaced;
            if (wordList.Count < numObj)
            {
                numObj = wordList.Count;
            }
            if (numObj > Bubblemap.Count)
            {
                int a = Bubblemap.Count;

                for (int i = a; i < numObj; i++)
                {
                    Array.Resize<string>(ref BubbleName, (int)numObj);
                    Array.Resize<string>(ref LabelName, (int)numObj);
                    BubbleName[i] = "Bubble" + i;
                    Bubblemap.Add(BubbleName[i], new MyPictureBox(100, 100, imageWidth, imageHeight));

                    LabelName[i] = "Label" + i;
                    Labelmap.Add(LabelName[i], new MyLabel("New Bubble", 100, 100, imageWidth, imageHeight));
                }
            }
            this.Controls.Clear();
            this.Controls.Add(myT);
            wordList = MixList(wordList);
            for (int i = 0; i < numObj; i++)
            {
                int myLabelHeight = (int)(0.5 * imageHeight);
                int myLabelWidth = (int)(0.83 * imageWidth);
                int myLabelXCoord = mypos[i, 0] + (int)(0.085 * imageWidth);
                int myLabelYCoord = mypos[i, 1] + (int)(0.25 * imageHeight);
                Labelmap[LabelName[i]].Text = wordList[i];
                Labelmap[LabelName[i]].SetBounds(myLabelXCoord, myLabelYCoord, myLabelWidth, myLabelHeight);
                Bubblemap[BubbleName[i]].SetBounds(mypos[i, 0], mypos[i, 1], imageWidth, imageHeight);


                this.Controls.Add(Labelmap[LabelName[i]]);
                this.Controls.Add(Bubblemap[BubbleName[i]]);
            }
        }

        private List<E> MixList<E>(List<E> inputList)
        {
            List<E> randomList = new List<E>(); Random r = new Random();
            int randomIndex = 0;
            while (inputList.Count > 0)
            {
                randomIndex = r.Next(0, inputList.Count); //Choose a random object in the list
                randomList.Add(inputList[randomIndex]); //add it to the new, random list
                inputList.RemoveAt(randomIndex); //remove to avoid duplicates
            } return randomList; //return the new random list
        }

        public void closeSaver(object sender, EventArgs e)
        {
            this.Close();
        }

        public void KWPload(object sender, EventArgs e)
        {
            ComboBox myYearCombo = getKWPCombo("YearCombo");
            ComboBox myTopicCombo = getKWPCombo("TopicCombo");

            myYearCombo.Text = Properties.Settings.Default.curYear;
            myTopicCombo.Text = Properties.Settings.Default.curTopic;

            getXMLYearData();
            getXMLTopicData(Properties.Settings.Default.curYear);

            myYearCombo.DataSource = yearList;
            myTopicCombo.DataSource = topicList;
        }

        private ComboBox getKWPCombo(string comboName)
        {
            Control[] myPossCombos = myKWP.Controls.Find(comboName, true);
            ComboBox myCombo = (ComboBox)myPossCombos[0];
            return myCombo;
        }

        public void KWP_Rem_Sup(object sender, EventArgs e)
        {
            supTopChange = false;
        }

        public void KWP_Topic_Change(object sender, EventArgs e)
        {   
            ComboBox myYearCombo = getKWPCombo("YearCombo");
            ComboBox myTopicCombo = getKWPCombo("TopicCombo");
            
            if (supTopChange != true && myTopicCombo.SelectedItem != null)
            {


                Properties.Settings.Default.curYear = myYearCombo.Text;
                Properties.Settings.Default.curTopic = myTopicCombo.SelectedItem.ToString();
                myTopicCombo.Text = Properties.Settings.Default.curTopic;
                Properties.Settings.Default.Save();

                string mynewtopic = Properties.Settings.Default.curTopic;
                setXMLLatestTopicData(Properties.Settings.Default.curYear, Properties.Settings.Default.curTopic);

                getXMLWordData(Properties.Settings.Default.curYear, mynewtopic);
                myT.Text = setTitleText();
                refreshScreen();
            }
            supTopChange = false;
        }

        public void KWP_Year_Change(object sender, EventArgs e)
        {
            supTopChange = true;
            topicList.Clear();
            ComboBox myYearCombo = getKWPCombo("YearCombo");
            ComboBox myTopicCombo = getKWPCombo("TopicCombo");

            Properties.Settings.Default.curYear = myYearCombo.Text;

            getXMLTopicData(Properties.Settings.Default.curYear);
            myTopicCombo.DataSource = null;
            myTopicCombo.Update();
            myTopicCombo.DataSource = topicList;
            myTopicCombo.Update();
            if (LatestTopic != "")
                myTopicCombo.Text = LatestTopic;
            else
                if (myTopicCombo.Items.Count > 0)
                    myTopicCombo.Text = myTopicCombo.Items[0].ToString();
                else
                    myTopicCombo.Text = "";
        }
	}
}
